package future;

import com.google.common.util.concurrent.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import util.Util;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author yding5
 */
public class ListenableFutureTest {

    private static CountDownLatch COUNT_DOWN;
    private static ListeningExecutorService EXECUTOR_SERVICE;

    @Before
    public void setup() {
        COUNT_DOWN = new CountDownLatch(3);
        EXECUTOR_SERVICE = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
    }

    @After
    public void cleanup() {
        EXECUTOR_SERVICE.shutdownNow();
    }

    @Test
    public void test() throws InterruptedException {
        Util.timeWatch();
        ListenableFuture<String> taskA = EXECUTOR_SERVICE.submit(() -> finishTaskA());
        Futures.addCallback(taskA, new FutureCallback<>() {
            @Override
            public void onSuccess(String A) {
                finishTaskC(A);
            }

            @Override
            public void onFailure(Throwable t) {}
        });

        ListenableFuture<String> taskB = EXECUTOR_SERVICE.submit(() -> finishTaskB());
        String A = getFuture(taskA);
        String B = getFuture(taskB);
        COUNT_DOWN.await();
        Util.timeWatch();
    }

    @Test
    public void callbackHell() throws InterruptedException {
        COUNT_DOWN = new CountDownLatch(4);
        Util.timeWatch();
        ListenableFuture<String> taskA = EXECUTOR_SERVICE.submit(() -> finishTaskA());
        Futures.addCallback(taskA, new FutureCallback<>() {
            @Override
            public void onSuccess(String A) {
                ListenableFuture<String> taskC = EXECUTOR_SERVICE.submit(() -> finishTaskC(A));
                Futures.addCallback(taskC, new FutureCallback<>() {
                    @Override
                    public void onSuccess(String C) {
                        finishTaskD(C);
                    }

                    @Override
                    public void onFailure(Throwable t) {}
                });
            }

            @Override
            public void onFailure(Throwable t) {}
        });

        ListenableFuture<String> taskB = EXECUTOR_SERVICE.submit(() -> finishTaskB());
        String A = getFuture(taskA);
        String B = getFuture(taskB);
        COUNT_DOWN.await();
        Util.timeWatch();
    }

    private static String finishTaskA() {
        Util.sleep(100L);
        Util.print("finish task A.");
        COUNT_DOWN.countDown();
        return "A";
    }

    private static String finishTaskB() {
        Util.sleep(200L);
        Util.print("finish task B.");
        COUNT_DOWN.countDown();
        return "B";
    }

    private static String finishTaskC(String A) {
        Util.sleep(300L);
        Util.print("finish task C based on " + A);
        COUNT_DOWN.countDown();
        return "C";
    }

    private static String finishTaskD(String C) {
        Util.sleep(400L);
        Util.print("finish task D based on " + C);
        COUNT_DOWN.countDown();
        return "C";
    }

    public static <T> T getFuture(Future<T> future) {
        try {
            return future.get(1, TimeUnit.SECONDS);
        } catch (Exception e) {
            future.cancel(true);
        }
        return null;
    }
}
